<?php

namespace application\model;

use core\CoreModel;
use ReflectionClass;
use core\entity\Page;
use core\entity\Sort;
use core\entity\Query;
use core\entity\Criteria;
use application\entity\DataPage;
use InvalidArgumentException;

class BaseModel extends CoreModel {

    private $entityClass;

    public function __construct($class) {
        $this->entityClass = $class;
        $reflection = new ReflectionClass($this->entityClass);
        parent::__construct($this->getTableName($reflection->getDocComment()));
    }

    private function recast($className, &$object) {
        if (!class_exists($className)) {
            throw new InvalidArgumentException(sprintf('Inexistant class %s.', $className));
        }
        $new = new $className();

        foreach ($object as $property => &$value) {
            $new->$property = &$value;
            unset($object->$property);
        }
        unset($value);
        $object = (unset) $object;
        return $new;
    }

    /**
     * Table name ReflectionClass class
     * @param type $string
     * @return type
     */
    private function getTableName($string = null) {
        preg_match('/\@document\((.*)\)/', $string, $matches);
        return @$matches[1];
    }

    /**
     * Truy vấn
     * @param Query $query
     * @return Result query
     */
    public function iQuery(Query $query) {
        return parent::query($query);
    }

    /**
     * tìm kiếm
     * @param Query $query
     * @param type $col
     * @return T
     */
    public function ifind(Query $query, $col = '*') {
        $tData = array();
        foreach (parent::find($query, $col) as $stdObject) {
            $tData[] = $this->recast($this->entityClass, $stdObject);
        }
        return $tData;
    }

    /**
     * Tìm kiếm 1 bản ghi
     * @param Query $query
     * @param type $col
     * @return T
     */
    public function ifindOne(Query $query, $col = '*') {
        $findOne = parent::findOne($query, $col);
        return $this->recast($this->entityClass, $findOne);
    }

    /**
     * Tìm theo id
     * @param type $id
     * @param type $col
     * @return array(T)
     */
    public function ifindById(/* String */ $id, $col = '*') {
        $findById = parent::findById($id);
        return $this->recast($this->entityClass, $findById);
    }

    /**
     * tìm kiếm theo danh sách id
     * @param type $ids
     * @param type $col
     * @return array(T)
     */
    public function ifindByIds($ids = array(), $col = '*') {
        $cri = new Criteria();
        $cri->andKey("id")->in($ids);
        $query = new Query($cri);
        $tData = array();
        foreach (parent::find($query, $col) as $stdObject) {
            $tData[] = $this->recast($this->entityClass, $stdObject);
        }
        return $tData;
    }

    /**
     * Xóa theo điều kiện 
     * 
     * @param Criteria $cri
     * @return boolean
     */
    public function idelete(Criteria $cri) {
        return parent::delete(new Query($cri));
    }

    /**
     * Xóa theo id
     * @param type $id
     * @return type
     */
    public function iremove($id) {
        $cri = new Criteria();
        $cri->andKey("id")->is($id);
        return $this->idelete($cri);
    }

    /**
     * Tìm kiếm theo điều kiện
     * 
     * @param Criteria $cri
     * @param Page $page
     * @param Sort $sort
     * @return \DataPage
     */
    public function iSearch(Criteria $cri, Page $page = null, Sort $sort = null) {
        if ($page == null) {
            $page = new Page();
        }
        $query = new Query();
        $dataPage = new DataPage();
        $dataPage->setDataCount($this->icount($cri));
        $query->query($cri);
        $query->withPage($page);
        if ($sort != null) {
            $query->withSort($sort);
        }
        $dataPage->setData($this->ifind($query));
        $dataPage->setPageIndex(floor($query->page->getOffset() / $query->page->getSize()) + 1);
        $dataPage->setPageSize($query->page->getSize());
        $dataPage->setPageCount(floor($dataPage->getDataCount() / $dataPage->getPageSize()));
        if ($dataPage->getDataCount() % $dataPage->getPageSize() != 0) {
            $dataPage->setPageCount($dataPage->getPageCount() + 1);
        }
        return $dataPage;
    }

    /**
     * Lưu data
     * @param T $params
     * @return type
     */
    public function isave($params) {
        $id = $params->id;
        unset($params->id);
        $reflectionClass = new ReflectionClass($params);
        foreach ($reflectionClass->getProperties() as $property) {
            if (preg_match('/\@remove/', $property->getDocComment())) {
                $pty = $property->getName();
                unset($params->$pty);
            }
        }
        $params = get_object_vars($params);
        if (isset($id) && $id != null && $id != '' && $this->iexists($id)) {
            $cri = new Criteria();
            $cri->andKey("id")->is($id);
            $query = new Query($cri);
            $this->update($query, $params);
            $params['id'] = $id;
        } else {
            if (isset($id) && $id != null && $id != '' && !$this->iexists($id)) {
                $params['id'] = $id;
            }
            $this->insert($params);
            $params['id'] = $this->getId();
        }
        return $this->recast($this->entityClass, $params);
    }

    /**
     * Count bản ghi
     * @param \core\entity\Criteria $cri
     * @return long
     */
    public function icount(Criteria $cri) {
        $query = new Query($cri);
        $data = parent::findOne($query, 'count(*) as total');
        return (int) $data->total;
    }

    /**
     * Kiểm tra id có tồn tại hay không
     * @param type $id
     * @return boole
     */
    public function iexists($id) {
        $cri = new Criteria();
        $cri->andKey("id")->is($id);
        $query = new Query($cri);
        return !($this->icount($cri) == 0);
    }

}
